/**
 * snooping.ts - 监视开播状态
 */

import * as vscode from 'vscode';
import * as biliapi from './api';
import { FollowsItem } from './followings';
import * as vschelper from './vscode-helper';


/**
 * 开播监视器实例
 *
 * NOTE: 创建实例后一定使用 await init(...) 初始化实例！
 */
class RoomSnoop {
  uid?: number;
  room_id?: number;
  /** @description user name */
  uname?: string;
  special?: boolean;
  hint: string = '';

  /** @description live_stutus on last check */
  last_status: boolean = false;

  /**
   * @constructor
   * @borrows config.vscdd.follows
   * @param [uid] - uid, room_id 至少提供一个
   * @param [room_id]
   * @throws {Error}
   *    * `not liver` - 该用户还未启用直播间
   *    * other
   */
  async init(uid?: number, room_id?: number) {
    if (typeof uid === 'undefined' && typeof room_id === 'undefined')
      throw new Error('one of uid, room_id must be given');
    if (typeof uid === 'undefined') {
      uid = await biliapi.get_uid_of_room(room_id!);
    }
    // load (and maybe update) from config
    let follows = vscode.workspace.getConfiguration('vscdd').get('follows') as FollowsItem[];
    let [found, config_updated] = [false, false];
    for (let f of follows) {
      if (f.uid !== uid)
        continue;
      found = true;
      if (typeof f.room_id === 'undefined') {
        f.room_id = await biliapi.get_room_of_uid(uid);
        if (typeof f.room_id === 'undefined')
          throw new Error('not liver');
        config_updated = true;
      }
      this.room_id = f.room_id;
      if (typeof f.uname === 'undefined') {
        f.uname = await biliapi.get_uname_of_uid(uid);
        config_updated = true;
      }
      this.uname = f.uname;
      this.special = typeof f.special !== 'undefined' && f.special;
      if (f.hint)
        this.hint = f.hint;
      break;
    }
    // finishing up
    if (!found)
      throw new Error('not found in vscdd.follows');
    if (config_updated)
      await vschelper.updateConfiguration('vscdd.follows', follows);
  }

  /**
   * 检查是否开播
   * @borrows this.last_status
   * @returns 是否正开播
   */
  async check_if_open(): Promise<boolean> {
    if (typeof this.room_id === 'undefined')
      throw new Error('call async init(...) before using the object');
    this.last_status = await biliapi.check_door_open(this.room_id);
    return this.last_status;
  }
}

let monitoring_uids: Map<number, [RoomSnoop, NodeJS.Timeout]> = new Map();

/**
 * 开始摸鱼/堇业监控
 * @borrows monitoring_uids
 * @param [uid] - 主播 UID，若为 undefined，则从 QuickPick UI 中选择
 * @param [is_startup] - 为初始化时调用，默认为否
 */
export async function startMonitoring(uids?: number[], is_startup=false) {
  // get uids if not given
  if (typeof uids === 'undefined') {
    const follows = vscode.workspace.getConfiguration('vscdd').get('follows') as FollowsItem[];
    let uidpool = new Map<number, undefined>();
    for (let {uid} of follows) {
      if (monitoring_uids.has(uid))
        continue;
      uidpool.set(uid, undefined);
    }
    if (uidpool.size === 0) {
      vscode.window.showInformationMessage('你已经 D 得够多了。。。');
      return;
    }
    // show picker
    let picks: (vscode.QuickPickItem & {uid: number})[] = [];
    for (let f of follows) {
      if (!uidpool.has(f.uid))
        continue;
      picks.push({label: `${f.uname}`, description: `UID: ${f.uid}`,
                  detail: f.hint ? f.hint : '', uid: f.uid});
    }
    const picked = await vscode.window.showQuickPick(
      picks,
      {
        canPickMany: true,
        matchOnDescription: true, matchOnDetail: true
      }
    );
    if (typeof picked === 'undefined')
      return;
    uids = [];
    for (let { uid } of picked)
      uids.push(uid);
  }
  // start monitors
  const prompt_startup: 'off' | 'all' | 'special' =
      vscode.workspace.getConfiguration('vscdd.snooping').get('promptOnStartup')!;
  for (let uid of uids) {
    if (monitoring_uids.has(uid))
      continue;
    let snooper = new RoomSnoop();
    try {
      await snooper.init(uid);
    }
    catch (e) {
      if (e.message! === 'not liver')
        continue;
      throw e;
    }
    await snooper.check_if_open();
    // initial prompt
    if (is_startup && prompt_startup !== 'off') {
      if (prompt_startup === 'special' && !snooper.special)
        continue;
      if (snooper.last_status) {
        vscode.window.showInformationMessage(`${snooper.uname} 正在直播！`);
      } else {
        vscode.window.showInformationMessage(`${snooper.uname} 正在摸鱼！`);
      }
    }
    // setup later jobs
    let to = setInterval(async () => {
      if (!snooper.last_status && await snooper.check_if_open())
        vscode.window.showInformationMessage(`${snooper.uname} 开门了！`);
      if (snooper.last_status && !await snooper.check_if_open())
        vscode.window.showInformationMessage(`${snooper.uname} 摸了！`);
      console.debug(`检查主播 ${snooper.uname} 开播状态：${snooper.last_status}`);
    }, 1000 * vscode.workspace.getConfiguration('vscdd.snooping').get<number>('interval')!);
    monitoring_uids.set(uid, [snooper, to]);
  }
}

/**
 * 关闭摸鱼/堇业监控
 * @borrows monitoring_uids
 * @param [uid] - 主播 UID，若不提供，则从 `monitoring_uids` 中使用 QuickPick UI 选择
 */
export async function stopMonitoring(uid?: number) {
  let stop_all = false;
  // pick UID if not specified
  if (typeof uid === 'undefined') {
    if (monitoring_uids.size === 0) {
      vscode.window.showInformationMessage('当前没有开启的监控。');
      return;
    }
    let picks: (vscode.QuickPickItem & {uid: number})[] = [];
    for (let [uid, [snp, _]] of monitoring_uids)
      picks.push({label: snp.uname!, description: `UID: ${uid}`,
                  detail: snp.hint, uid: uid});
    picks.push({label: '所有', description: 'all', uid: -1})
    let picked = await vscode.window.showQuickPick(
      picks,
      {
        canPickMany: false, placeHolder: '选择“所有/all”以取消所有监控',
        matchOnDescription: true, matchOnDetail: true
      }
    );
    if (typeof picked === 'undefined')
      return;
    if (picked.uid === -1)
      stop_all = true;
    else
      uid = picked.uid;
  }

  if (stop_all) {
    for (let [_, [__, to]] of monitoring_uids)
      clearInterval(to);
    monitoring_uids.clear();
    return;
  }

  let [_, to] = monitoring_uids.get(uid!)!;
  clearInterval(to);
  monitoring_uids.delete(uid!);
}

export function tearDownSnooping() {
  for (let [_, [__, to]] of monitoring_uids)
    clearInterval(to);
  monitoring_uids.clear();
}
